package com.wzdigit.wms.wms.service.basic;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.wzdigit.framework.Shift;
import com.wzdigit.wms.basic.client.PagerInfo;
import com.wzdigit.wms.basic.client.request.basic.*;
import com.wzdigit.wms.basic.client.response.Result;
import com.wzdigit.wms.basic.client.response.basic.*;
import com.wzdigit.wms.basic.domain.basic.Location;
import com.wzdigit.wms.basic.domain.basic.Zone;
import com.wzdigit.wms.basic.enums.BaseIsEnabledEnum;
import com.wzdigit.wms.basic.enums.LocationEnum;
import com.wzdigit.wms.basic.mapper.core.basic.LocationMapper;
import com.wzdigit.wms.basic.mapper.core.basic.ZoneMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsMapper;
import com.wzdigit.wms.common.entity.ExcelExportConfig;
import com.wzdigit.wms.common.utils.HttpExcel;
import com.wzdigit.wms.basic.util.HttpSessionUtil;
import com.wzdigit.wms.basic.util.StringUtils;
import com.wzdigit.wms.common.utils.ValidatorUtil;
import com.wzdigit.wms.basic.util.common.SeparatorString;
import com.wzdigit.wms.common.entity.StatusCode;
import net.dreamlu.mica.core.beans.MicaBeanCopier;
import net.dreamlu.mica.core.convert.MicaConverter;
import net.dreamlu.mica.core.utils.BeanUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 库位 服务类
 * @author yangdongjie
 * @LocationAttrService.java
 * @2021年1月4日上午11:16:31
 */
@Service
public class LocationService {

    @Autowired
    private LocationMapper locationMapper;
    
    @Autowired
    private GoodsMapper goodsMapper;
    
    @Autowired
    private ZoneMapper zoneMapper;
    
    private static final int USE_CODE = BaseIsEnabledEnum.USE.code;
    private static final int DISABLE_CODE = BaseIsEnabledEnum.DISABLE.code;

    public List<LocationApiResponse> list() {
		return locationMapper.selectAllByStatus(USE_CODE);
    }
    
    public LocationResponse getLocation(Integer locId) {
    	return locationMapper.selectOneByLocId(locId);
    }
    
	public List<LocationApiResponse> getLocationByZone(Integer zoneId) {
		return locationMapper.selectAllByZoneIdAndStatus(zoneId, USE_CODE);
    }
	
	public List<LocationApiResponse> getLocationByWhs(Integer whsId) {
		return locationMapper.selectAllByWhsIdAndStatus(whsId, USE_CODE);
    }
    
	@SuppressWarnings("rawtypes")
	public PagerInfo<List<LocationResponse>> searchLocation(SearchLocationRequest dto) {
    	Page page = PageHelper.offsetPage(dto.getSkipCount(), dto.getMaxResultCount());
        PagerInfo<List<LocationResponse>> pagerInfo = new PagerInfo<List<LocationResponse>>();
        List<LocationResponse> list = locationMapper.selectAll(dto);
        pagerInfo.setItems(list);
        pagerInfo.setTotalCount(page.getTotal());
		return pagerInfo;
    }
    
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public LocationResponse createLocation(CreateLocationRequest dto,
    		HttpServletRequest servletRequest) {
    	String loginName = HttpSessionUtil.getLoginName(servletRequest);
    	Location location = locationMapper.selectOneByLocCode(dto.getLocCode());
    	if(location != null) {
    		// 编码已存在
    		Shift.fatal(StatusCode.CODE_DATA_EXIST);
    	}
    	location = new Location();
    	BeanUtil.copyNonNull(dto, location);
    	location.setCreateUser(loginName);
    	location.setCreateTime(new Date());
    	locationMapper.insert(location);
    	
    	LocationResponse response = new LocationResponse();
    	BeanUtil.copyNonNull(location, response);
		return response;
    }
    
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public LocationResponse updateLocation(UpdateLocationRequest dto,
    		HttpServletRequest servletRequest) {
    	String loginName = HttpSessionUtil.getLoginName(servletRequest);
    	Location location = locationMapper.selectOneByLocCode(dto.getLocCode());
    	if(location != null) {
    		if (dto.getLocId().intValue() != location.getLocId().intValue()) {
    			// 编码已存在
    			Shift.fatal(StatusCode.CODE_DATA_EXIST);
			}
    	}
    	location = locationMapper.selectByPrimaryKey(dto.getLocId());
    	BeanUtil.copy(dto, location);
    	location.setModifyUser(loginName);
    	location.setModifyTime(new Date());
    	locationMapper.updateById(location);
    	
    	LocationResponse response = new LocationResponse();
    	BeanUtil.copy(location, response);
		return response;
    }
    
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public <T> Result<T> updateStatus(UpdateStatusLocationRequest dto,
    		HttpServletRequest servletRequest) {
    	String loginName = HttpSessionUtil.getLoginName(servletRequest);
    	Location location = locationMapper.selectByPrimaryKey(dto.getLocId());
    	location.setStatus(dto.getStatus());
    	location.setModifyUser(loginName);
    	location.setModifyTime(new Date());
    	locationMapper.updateStatus(location);
		return new Result<>();
    }
    
    
	@SuppressWarnings("rawtypes")
	public void exportBasic(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {
		List datas = new ArrayList(0);
		
		List<ExcelExportConfig> configs = new ArrayList<>(17);
		configs.add(new ExcelExportConfig("库位代码", "locCode", 120));
		configs.add(new ExcelExportConfig("库位描述", "locDesc", 120));
		configs.add(new ExcelExportConfig("库位显示", "locDisp", 120));
		configs.add(new ExcelExportConfig("电子标签ID", "rfidId", 120));
		configs.add(new ExcelExportConfig("库区编码", "zoneCode", 120));

		configs.add(new ExcelExportConfig("库位类型", "udf2", 120));
		configs.add(new ExcelExportConfig("属性", "udf3", 120));
		configs.add(new ExcelExportConfig("使用方式", "udf1", 120));
		configs.add(new ExcelExportConfig("周转需求", "udf5", 120));
		configs.add(new ExcelExportConfig("库位处理", "udf4", 120));

		configs.add(new ExcelExportConfig("校验码", "validationCode", 120));
		configs.add(new ExcelExportConfig("货架ID", "rackId", 120));
		configs.add(new ExcelExportConfig("上架顺序", "putawaySeq", 120));
		configs.add(new ExcelExportConfig("拣货顺序", "pickSeq", 120));
		configs.add(new ExcelExportConfig("存储环境", "storeEnv", 120));

		configs.add(new ExcelExportConfig("备注", "comments", 120));
		configs.add(new ExcelExportConfig("状态（启用/停用）", "status", 120));

		export("库位基础信息", datas, configs, servletRequest, servletResponse);
	}
	
	@SuppressWarnings("rawtypes")
	public void exportLimit(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {
		List datas = new ArrayList(0);

		List<ExcelExportConfig> configs = new ArrayList<>(10);
		configs.add(new ExcelExportConfig("库位代码", "locCode", 120));
		configs.add(new ExcelExportConfig("重量限制", "weightCapacity", 120));
		configs.add(new ExcelExportConfig("体积限制", "cubicCapacity", 120));
		configs.add(new ExcelExportConfig("数量限制", "countCapacity", 120));
		configs.add(new ExcelExportConfig("托盘数量限制", "pltCount", 120));

		configs.add(new ExcelExportConfig("箱子数量限制", "csCount", 120));
		configs.add(new ExcelExportConfig("SKU限制", "skuCapacity", 120));
		configs.add(new ExcelExportConfig("忽略ID（是/否）", "loseIdFlag", 120));
		configs.add(new ExcelExportConfig("允许混放产品（是/否）", "mixFlag", 120));
		configs.add(new ExcelExportConfig("允许混放批次（是/否）", "mixLotFlag", 120));

		export("库位限制信息", datas, configs, servletRequest, servletResponse);
	}

	@SuppressWarnings("rawtypes")
	public void exportSize(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {
		List datas = new ArrayList(0);
		
		List<ExcelExportConfig> configs = new ArrayList<>(4);
		configs.add(new ExcelExportConfig("库位代码", "locCode", 120));
		configs.add(new ExcelExportConfig("长", "length", 120));
		configs.add(new ExcelExportConfig("宽", "weidth", 120));
		configs.add(new ExcelExportConfig("高", "height", 120));

		export("库位尺寸信息", datas, configs, servletRequest, servletResponse);
	}
	
	@SuppressWarnings("rawtypes")
	public void exportCoord(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {
		List datas = new ArrayList(0);
		
		List<ExcelExportConfig> configs = new ArrayList<>(7);
		configs.add(new ExcelExportConfig("库位代码", "locCode", 120));
		configs.add(new ExcelExportConfig("X轴坐标", "xcoord", 120));
		configs.add(new ExcelExportConfig("Y轴坐标", "ycoord", 120));
		configs.add(new ExcelExportConfig("Z轴坐标", "zcoord", 120));
		configs.add(new ExcelExportConfig("X像素", "xpixel", 120));
		configs.add(new ExcelExportConfig("Y像素", "ypixel", 120));
		configs.add(new ExcelExportConfig("层数", "locLevel", 120));

		export("库位坐标信息", datas, configs, servletRequest, servletResponse);
	}
    
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void export(String fileName, List datas, List<ExcelExportConfig> configs,
    		HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {
		OutputStream outputStream = null;
		try {
			XSSFWorkbook wb = HttpExcel.newListToExcel(datas, configs);
			servletResponse.setContentType("application/octet-stream");
			String userAgent = servletRequest.getHeader("USER-AGENT");
			if (userAgent.contains("Mozilla")) {
				fileName = new String(fileName.getBytes(), "ISO8859-1");
			} else {
				fileName = URLEncoder.encode(fileName, "utf8");
			}
			servletResponse.setHeader("Content-Disposition", "attachment;fileName=" + fileName + ".xlsx");
			servletResponse.flushBuffer();
			outputStream = servletResponse.getOutputStream();
			wb.write(outputStream);
			outputStream.flush();
		} catch (Exception e) {
			throw e;
		} finally {
			if (outputStream != null) {
				outputStream.close();
			}
		}
	}
	
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public ImportLocationResponse importCoord(
			MultipartFile file, 
			HttpServletRequest servletRequest, 
			HttpServletResponse servletResponse) throws Exception {
		String loginName = HttpSessionUtil.getLoginName(servletRequest);
		List<ImportLocationCoordRequest> excel = null;
		try {
    		InputStream inputStream = file.getInputStream();
    		
    		String[] properties = {"locCode", "xcoord", "ycoord", "zcoord", 
    				"locLevel", "xpixel", "ypixel"};
    		excel = HttpExcel.importExcel(inputStream, ImportLocationCoordRequest.class, properties);
		} catch (Exception e) {
			// 模板错误，请确认
			Shift.fatal(StatusCode.IMPORT_DATA_ERROR);
		}
    	
		if (excel == null || excel.size() == 0) {
			// 该模板数据为空
			Shift.fatal(StatusCode.IMPORT_DATA_NULL);
		}
		
		// 失败、成功条数
		int fail = excel.size(), sucess = 0;
		// 失败记录集合
		List<ImportLocationErrorResponse> errors = new ArrayList<>();
		
		MicaConverter micaConverter = new MicaConverter(
				ImportLocationCoordRequest.class, Location.class);
		MicaBeanCopier copier = MicaBeanCopier.create(
				ImportLocationCoordRequest.class, Location.class, true);
		
		for (ImportLocationCoordRequest dto : excel) {
			boolean flag = true;
			
			Location location = null;
			
			// 1：检验参数
			if(StringUtils.isNull(dto.getLocCode())) {
				ImportLocationErrorResponse error = new ImportLocationErrorResponse();
				BeanUtil.copyNonNull(dto, error);
				
				StringBuilder errorDec = new StringBuilder("【第");
				errorDec.append(dto.getRow()).append("行】").append("库位编码不能为空");
				error.setRowMessage(errorDec.toString());
				
				errors.add(error);
				flag = false;
			}
			
			if (flag) {
				// 2：库位编码是否有效
				location = locationMapper.selectOneByLocCode(dto.getLocCode());
				if (location == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("库位编码无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}
			
			// 3：X轴坐标是否有效
			if(StringUtils.isNotNull(dto.getXcoord())) {
				if (!StringUtils.isInteger(dto.getXcoord())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("X轴坐标无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			// 4：Y轴坐标是否有效
			if(StringUtils.isNotNull(dto.getYcoord())) {
				if (!StringUtils.isInteger(dto.getYcoord())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("Y轴坐标无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			// 5：Z轴坐标是否有效
			if(StringUtils.isNotNull(dto.getZcoord())) {
				if (!StringUtils.isInteger(dto.getZcoord())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("Z轴坐标无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			// 6：层数是否有效
			if(StringUtils.isNotNull(dto.getLocLevel())) {
				if (!StringUtils.isInteger(dto.getLocLevel())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("层数无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			// 7：X像素是否有效
			if(StringUtils.isNotNull(dto.getXpixel())) {
				if (!StringUtils.isInteger(dto.getXpixel())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("X像素无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			// 8：Y像素是否有效
			if(StringUtils.isNotNull(dto.getYpixel())) {
				if (!StringUtils.isInteger(dto.getYpixel())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("Y像素无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			if (flag) {
				copier.copy(dto, location, micaConverter);
				location.setModifyUser(loginName);
				location.setModifyTime(new Date());
				locationMapper.updateById(location);
				--fail;
				++sucess;
			}
		}
		ImportLocationResponse response = new ImportLocationResponse();
		response.setFailureCount(fail);
		response.setSucceessCount(sucess);
		response.setItems(errors);
		return response;
	}
	
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public ImportLocationResponse importSize(
			MultipartFile file, 
			HttpServletRequest servletRequest, 
			HttpServletResponse servletResponse) throws Exception {
		String loginName = HttpSessionUtil.getLoginName(servletRequest);
		List<ImportLocationSizeRequest> excel = null;
		try {
    		InputStream inputStream = file.getInputStream();
    		
    		String[] properties = {"locCode", "length", "weidth", "height"};
    		excel = HttpExcel.importExcel(inputStream, ImportLocationSizeRequest.class, properties);
		} catch (Exception e) {
			// 模板错误，请确认
			Shift.fatal(StatusCode.IMPORT_DATA_ERROR);
		}
		if (excel == null || excel.size() == 0) {
			// 该模板数据为空
			Shift.fatal(StatusCode.IMPORT_DATA_NULL);
		}
		
		// 失败、成功条数
		int fail = excel.size(), sucess = 0;
		// 失败记录集合
		List<ImportLocationErrorResponse> errors = new ArrayList<>();
		
		MicaConverter micaConverter = new MicaConverter(
				ImportLocationSizeRequest.class, Location.class);
		MicaBeanCopier copier = MicaBeanCopier.create(
				ImportLocationSizeRequest.class, Location.class, true);
		
		for (ImportLocationSizeRequest dto : excel) {
			boolean flag = true;
			
			Location location = null;
			
			// 1：检验参数
			if(StringUtils.isNull(dto.getLocCode())) {
				ImportLocationErrorResponse error = new ImportLocationErrorResponse();
				BeanUtil.copyNonNull(dto, error);
				
				StringBuilder errorDec = new StringBuilder("【第");
				errorDec.append(dto.getRow()).append("行】").append("库位编码不能为空");
				error.setRowMessage(errorDec.toString());
				
				errors.add(error);
				flag = false;
			}
			
			if (flag) {
				// 2：库位编码是否有效
				location = locationMapper.selectOneByLocCode(dto.getLocCode());
				if (location == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("库位编码无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}
			
			// 3：长是否有效
			if(StringUtils.isNotNull(dto.getLength())) {
				if (!StringUtils.isInteger(dto.getLength())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("长无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			// 4：宽是否有效
			if(StringUtils.isNotNull(dto.getWeidth())) {
				if (!StringUtils.isInteger(dto.getWeidth())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("宽无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			// 5：高是否有效
			if(StringUtils.isNotNull(dto.getHeight())) {
				if (!StringUtils.isInteger(dto.getHeight())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);
					
					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("高无效");
					error.setRowMessage(errorDec.toString());
					
					errors.add(error);
					flag = false;
				}
			}
			
			if (flag) {
				copier.copy(dto, location, micaConverter);
				location.setModifyUser(loginName);
				location.setModifyTime(new Date());
				locationMapper.updateById(location);
				--fail;
				++sucess;
			}
		}
		ImportLocationResponse response = new ImportLocationResponse();
		response.setFailureCount(fail);
		response.setSucceessCount(sucess);
		response.setItems(errors);
		return response;
	}
	
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public ImportLocationResponse importLimit(
			MultipartFile file, 
			HttpServletRequest servletRequest, 
			HttpServletResponse servletResponse) throws Exception {
		String loginName = HttpSessionUtil.getLoginName(servletRequest);
		List<ImportLocationLimitRequest> excel = null;
		try {
    		InputStream inputStream = file.getInputStream();
    		
    		String[] properties = {"locCode", "weightCapacity", "cubicCapacity", "countCapacity", 
    				"pltCount", "csCount", "skuCapacity", "loseIdFlag", "mixFlag", "mixLotFlag"};
    		excel = HttpExcel.importExcel(inputStream, ImportLocationLimitRequest.class, properties);
		} catch (Exception e) {
			// 模板错误，请确认
			Shift.fatal(StatusCode.IMPORT_DATA_ERROR);
		}
		if (excel == null || excel.size() == 0) {
			// 该模板数据为空
			Shift.fatal(StatusCode.IMPORT_DATA_NULL);
		}
		
		// 失败、成功条数
		int fail = excel.size(), sucess = 0;
		// 失败记录集合
		List<ImportLocationErrorResponse> errors = new ArrayList<>();
		
		MicaConverter micaConverter = new MicaConverter(
				ImportLocationLimitRequest.class, Location.class);
		MicaBeanCopier copier = MicaBeanCopier.create(
				ImportLocationLimitRequest.class, Location.class, true);
		
		for (ImportLocationLimitRequest dto : excel) {
			boolean flag = true;
			
			Location location = null;
			
			// 1：检验参数
			for (String message : ValidatorUtil.validatorMessages(dto)) {
				ImportLocationErrorResponse error = new ImportLocationErrorResponse();
				BeanUtil.copyNonNull(dto, error);
				
				StringBuilder errorDec = new StringBuilder("【第");
				errorDec.append(dto.getRow()).append("行】").append(message);
				error.setRowMessage(errorDec.toString());
				
				errors.add(error);
				flag = false;
			}
			
			if (flag) {
				// 2：库位编码是否有效
				location = locationMapper.selectOneByLocCode(dto.getLocCode());
				if (location == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("库位编码无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 3：重量限制是否有效
				if (!StringUtils.isNumber(dto.getWeightCapacity())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("重量限制无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 4：体积限制是否有效
				if (!StringUtils.isNumber(dto.getCubicCapacity())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("体积限制无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 5：数量限制是否有效
				if (!StringUtils.isInteger(dto.getCountCapacity())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("数量限制无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 6：托盘数量限制是否有效
				if (!StringUtils.isInteger(dto.getPltCount())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("托盘数量限制无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 7：箱子数量限制是否有效
				if (!StringUtils.isInteger(dto.getCsCount())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("箱子数量限制无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 8：SKU限制是否有效
				if (!StringUtils.isInteger(dto.getSkuCapacity())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("SKU限制无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}
			
			// 9：忽略 ID是否有效
			if (StringUtils.isNotNull(dto.getLoseIdFlag())) {
				if (!"是".equals(dto.getLoseIdFlag()) && !"否".equals(dto.getLoseIdFlag())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("忽略 ID无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}
			
			// 10：允许混放产品是否有效
			if (StringUtils.isNotNull(dto.getMixFlag())) {
				if (!"是".equals(dto.getMixFlag()) && !"否".equals(dto.getMixFlag())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("允许混放产品无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}
			
			// 11：允许混放批次是否有效
			if (StringUtils.isNotNull(dto.getMixLotFlag())) {
				if (!"是".equals(dto.getMixLotFlag()) && !"否".equals(dto.getMixLotFlag())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("允许混放批次无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}
			
			if (flag) {
				// 设置忽略 ID
				if ("是".equals(dto.getLoseIdFlag())) {
					dto.setLoseIdFlag("1");
				} else {
					dto.setLoseIdFlag("0");
				}
				
				// 设置允许混放产品
				if ("是".equals(dto.getMixFlag())) {
					dto.setMixFlag("1");
				} else {
					dto.setMixFlag("0");
				}
				
				// 设置允许混放批次
				if ("是".equals(dto.getMixLotFlag())) {
					dto.setMixLotFlag("1");
				} else {
					dto.setMixLotFlag("0");
				}
				
				copier.copy(dto, location, micaConverter);
				location.setModifyUser(loginName);
				location.setModifyTime(new Date());
				locationMapper.updateById(location);
				--fail;
				++sucess;
			}
		}
		ImportLocationResponse response = new ImportLocationResponse();
		response.setFailureCount(fail);
		response.setSucceessCount(sucess);
		response.setItems(errors);
		return response;
	}
    
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public ImportLocationResponse importBasic(
    		MultipartFile file, 
			HttpServletRequest servletRequest, 
			HttpServletResponse servletResponse) throws Exception {
		String loginName = HttpSessionUtil.getLoginName(servletRequest);
		List<ImportLocationBasicRequest> excel = null;
		try {
			InputStream inputStream = file.getInputStream();

			String[] properties = { "locCode", "locDesc", "locDisp", "rfidId", "zoneCode", "udf2", "udf3", "udf1",
					"udf5", "udf4", "validationCode", "rackId", "putawaySeq", "pickSeq", "storeEnv", "comments",
					"status" };
			excel = HttpExcel.importExcel(inputStream, ImportLocationBasicRequest.class, properties);
		} catch (Exception e) {
			// 模板错误，请确认
			Shift.fatal(StatusCode.IMPORT_DATA_ERROR);
		}
		
		if (excel == null || excel.size() == 0) {
			// 该模板数据为空
			Shift.fatal(StatusCode.IMPORT_DATA_NULL);
		}

		// 失败、成功条数
		int fail = excel.size(), sucess = 0;
		// 失败记录集合
		List<ImportLocationErrorResponse> errors = new ArrayList<>();

		// 使用方式/用途
		Set<Entry<Integer, String>> locUsages = LocationEnum.getEnumByDescription("LocUsage").entrySet();
		// 库位类型
		Set<Entry<Integer, String>> locTypes = LocationEnum.getEnumByDescription("LocType").entrySet();
		// 库位属性
		Set<Entry<Integer, String>> locAttrs = LocationEnum.getEnumByDescription("LocAttr").entrySet();
		// 库位处理
		Set<Entry<Integer, String>> locHandlings = LocationEnum.getEnumByDescription("LocHandling").entrySet();
		// 周转需求
		Set<Entry<Integer, String>> demands = LocationEnum.getEnumByDescription("Demand").entrySet();

		for (ImportLocationBasicRequest dto : excel) {
			boolean flag = true;
			Zone zone = null;
			Location location = null;

			Integer locUsage = null;
			Integer locType = null;
			Integer locAttr = null;

			// 1：检验参数
			for (String message : ValidatorUtil.validatorMessages(dto)) {
				ImportLocationErrorResponse error = new ImportLocationErrorResponse();
				BeanUtil.copyNonNull(dto, error);

				StringBuilder errorDec = new StringBuilder("【第");
				errorDec.append(dto.getRow()).append("行】").append(message);
				error.setRowMessage(errorDec.toString());

				errors.add(error);
				flag = false;
			}

			if (flag) {
				// 2：库区编码是否有效
				zone = zoneMapper.selectOneByZoneCode(dto.getZoneCode());
				if (zone == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("库区编码无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 3：编码是否有效
				location = locationMapper.selectOneByLocCode(dto.getLocCode());
				if (location != null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append(StatusCode.CODE_DATA_EXIST.message());
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 5：状态值是否有效
				if (!"启用".equals(dto.getStatus()) && !"停用".equals(dto.getStatus())) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("状态值无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 6：使用方式是否有效
				for (Entry<Integer, String> entry : locUsages) {
					if (dto.getUdf1().equals(entry.getValue())) {
						locUsage = entry.getKey();
						break;
					}
				}
				if (locUsage == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("使用方式无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 7：库位类型是否有效
				for (Entry<Integer, String> entry : locTypes) {
					if (dto.getUdf2().equals(entry.getValue())) {
						locType = entry.getKey();
						break;
					}
				}
				if (locType == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("库位类型无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}

				// 8：库位属性是否有效
				for (Entry<Integer, String> entry : locAttrs) {
					if (dto.getUdf3().equals(entry.getValue())) {
						locAttr = entry.getKey();
						break;
					}
				}
				if (locAttr == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("库位属性无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}

			// 9：电子标签是否有效【逻辑待确认】
			/*if (StringUtils.isNotNull(dto.getRfidId())) {
				ImportLocationErrorResponse error = new ImportLocationErrorResponse();
				BeanUtil.copyNonNull(dto, error);
				
				StringBuilder errorDec = new StringBuilder("【第");
				errorDec.append(dto.getRow()).append("行】").append("电子标签ID无效");
				error.setRowMessage(errorDec.toString());
				
				errors.add(error);
				flag = false;
			}*/

			// 10：货架是否有效【逻辑待确认】
			/*if (StringUtils.isNotNull(dto.getRackId())) {
				ImportLocationErrorResponse error = new ImportLocationErrorResponse();
				BeanUtil.copyNonNull(dto, error);
				
				StringBuilder errorDec = new StringBuilder("【第");
				errorDec.append(dto.getRow()).append("行】").append("货架无效");
				error.setRowMessage(errorDec.toString());
				
				errors.add(error);
				flag = false;
			}*/

			// 11：库位处理是否有效
			Integer locHandling = null;
			if (StringUtils.isNotNull(dto.getUdf4())) {
				for (Entry<Integer, String> entry : locHandlings) {
					if (dto.getUdf4().equals(entry.getValue())) {
						locHandling = entry.getKey();
						break;
					}
				}
				if (locHandling == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("库位处理无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}

			// 12：周转需求是否有效
			Integer demand = null;
			if (StringUtils.isNotNull(dto.getUdf5())) {
				for (Entry<Integer, String> entry : demands) {
					if (dto.getUdf5().equals(entry.getValue())) {
						demand = entry.getKey();
						break;
					}
				}
				if (demand == null) {
					ImportLocationErrorResponse error = new ImportLocationErrorResponse();
					BeanUtil.copyNonNull(dto, error);

					StringBuilder errorDec = new StringBuilder("【第");
					errorDec.append(dto.getRow()).append("行】").append("周转需求无效");
					error.setRowMessage(errorDec.toString());

					errors.add(error);
					flag = false;
				}
			}

			if (flag) {
				location = new Location();
				BeanUtil.copyNonNull(dto, location);
				location.setCreateUser(loginName);
				location.setCreateTime(new Date());

				location.setZoneId(zone.getZoneId());
				location.setLocUsage(locUsage);
				location.setLocType(locType);
				location.setLocAttr(locAttr);
				location.setLocHandling(locHandling);
				location.setDemand(demand);

				location.setWeightCapacity(new BigDecimal(0));
				location.setCubicCapacity(new BigDecimal(0));
				location.setCountCapacity(0);
				location.setPltCount(0);
				location.setCsCount(0);
				location.setSkuCapacity(0);
				location.setPutawaySeq("/");
				location.setPickSeq("/");

				// 设置状态值
				if ("启用".equals(dto.getStatus())) {
					location.setStatus(USE_CODE);
				} else {
					location.setStatus(DISABLE_CODE);
				}
				locationMapper.insert(location);
				--fail;
				++sucess;
			}
		}

		ImportLocationResponse response = new ImportLocationResponse();
		response.setFailureCount(fail);
		response.setSucceessCount(sucess);
		response.setItems(errors);
		return response;
	}
	
	/**
	 * 根据库位获取仓库+库位信息
	 * @param locCode
	 * @return
	 */
	public RecommendedLocationResponse selectWareHouseByLocCode(String locCode) {
		return locationMapper.selectWareHouseByLocCodeAndStatus(locCode, USE_CODE);
	}
	
	/**
	 * 获取推荐上架库位
	 * @param goodsSn
	 * @return
	 * 
	 * 推荐规则为：
	 * 1、同仓库、同物料、同PO
	 * 2、同仓库取3个库位
	 */
	public String selectRecommendedUpLocation(Integer skuId, Integer whsId, String poNo) {
		try {
			List<Integer> locList = goodsMapper.selectLocIdListBySkuIdAndWhsIdOrPoNo(skuId, whsId, poNo);
			
			List<String> locCodeList = null;
			if(locList.size() > 0) {
				locCodeList = locationMapper.selectLocCodeListByLocIdList(locList);
			} else {
				locCodeList = locationMapper.selectLocCodeListByWhsId(3, whsId);
			}
			
			if(locCodeList.size() > 0) {
				StringBuffer locCodeSb = new StringBuffer();
				for (String locCode : locCodeList) {
					locCodeSb.append(locCode).append(SeparatorString.COMMA);
				}
				locCodeSb.deleteCharAt(locCodeSb.length() - 1);
				return locCodeSb.toString();
			} else {
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 根据库区ID查询
	 * @param areaId
	 * @return
	 */
	public List<Location> selectLocationListByAreaId(Integer areaId) {

		//创建类型的条件构造器
		QueryWrapper<Location> wrapper = new QueryWrapper<>();
		wrapper.eq("ZONE_ID", areaId);
		List<Location> locationList = locationMapper.selectList(wrapper);
		return locationList;
	}

	/**
	 * 根据主键找实体
	 * @param locId
	 * @return
	 */
	public Location getLocationByLocId(Integer locId) {
		if(locId == null) {
			return null;
		}
		Location location = locationMapper.selectById(locId);
		return location;
	}

	public List<Location> selectByLocIdList(List<Integer> locIdListOth) {
		//创建类型的条件构造器
		QueryWrapper<Location> wrapper = new QueryWrapper<>();
		wrapper.in("LOC_ID",locIdListOth);

		List<Location> locationList = locationMapper.selectList(wrapper);
		return locationList;
	}
}
